package top.lunarye.handler.deduplication.limit;

import cn.hutool.core.util.IdUtil;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;
import top.lunarye.common.domaim.TaskInfo;
import top.lunarye.handler.deduplication.DeduplicationParam;
import top.lunarye.handler.deduplication.service.AbstractDeduplicationService;
import top.lunarye.support.utils.RedisUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 滑动窗口去重器
 * 用于一天内N次相同渠道去重
 * @author lunar
 * @since 2022-07-20
 */
@Service(value = "SlideWindowLimitService")
public class SlideWindowLimitService extends AbstractLimitService {

    private static final String LIMIT_TAG = "SW_";

    @Resource
    private RedisUtils redisUtils;

    private DefaultRedisScript<Long> redisScript;

    @PostConstruct
    public void init() {
        redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(Long.class);
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("limit.lua")));
    }

    @Override
    public Set<String> limitFilter(AbstractDeduplicationService service, TaskInfo taskInfo, DeduplicationParam param) {
        Set<String> filterReceiver = new HashSet<>(taskInfo.getReceiver().size());
        long nowTime = System.currentTimeMillis();
        for (var receiver : taskInfo.getReceiver()) {
            String key = LIMIT_TAG + deduplicationSingleKey(service, taskInfo, receiver);
            // 在这里使用雪花id保证生成的是全局唯一的id
            String scoreValue = String.valueOf(IdUtil.getSnowflake().nextId());
            String score = String.valueOf(nowTime);
            // 使用zset存储生成的雪花id，通过为key建立zset来实现滑动窗口去重
            if (redisUtils.execLimitLua(redisScript, List.of(key), String.valueOf(param.getDeduplicationTime() * 1000),
                    score, String.valueOf(param.getCountNum()), scoreValue)) {
                filterReceiver.add(receiver);
            }
        }
        return filterReceiver;
    }
}
